/*
 *  Author: zhaijl 
 *  Url: http://218.1.73.212/down.php
 *  Country: China
 *  
 * ====================================================================
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * ====================================================================
 *
 * This software based apache mina 2.0 and spring 3.0
 *
 */
package org.cloudy.http.codec;

import java.io.BufferedReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.HashMap;
import java.util.Map;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

public class Decoder implements ProtocolDecoder {
	private CharsetDecoder decoder = Charset.defaultCharset ().newDecoder () ;
	
	public Decoder () {}
	
    public void decode ( IoSession iosession, IoBuffer iobuffer, ProtocolDecoderOutput protocoldecoderoutput ) 
    	throws Exception {
    	Request m = decodeBody ( iobuffer ) ;

        if ( m != null ) protocoldecoderoutput.write ( m ) ;
    }
    
    public void finishDecode ( IoSession iosession, ProtocolDecoderOutput protocoldecoderoutput )
		throws Exception {}
    
    public void dispose ( IoSession iosession ) throws Exception {}	
    
    private Request decodeBody ( IoBuffer in ) throws Exception {
    	Request req = new Request();
        try {
        	req.setHeaders ( parseRequest ( new StringReader ( in.getString ( decoder ) ) ) ) ;
            return req ;
        } catch ( Exception ex ) {
            throw ex ;
        }
    }

    private Map <String, String[]> parseRequest ( Reader is ) throws Exception {
        Map < String, String[] > map = new HashMap < String, String[] > () ;
        BufferedReader rdr = new BufferedReader ( is ) ;
        
        String line = rdr.readLine () ;// Get request URL.
        String[] url = line.split ( " " ) ;
        if ( url.length < 3 ) return map ;

        map.put ( "URI", new String[] { line } ) ;
        map.put ( "Method", new String[] { url[0].toUpperCase () } ) ;
        map.put ( "Context", new String[] { url[1].substring ( 1 ) } ) ;
        map.put ( "Protocol", new String[] { url[2] } ) ;
        // Read header
        while ( ( line = rdr.readLine () ) != null && line.length () > 0 ) {          	
            String[] tokens = line.split ( ": " ) ;
            if ( tokens[0].endsWith ( "Cookie" ) )
            	map.put ( tokens[0], tokens[1].split ( ";" ) ) ;
            else
            	map.put ( tokens[0], tokens[1].split ( "," ) ) ;
        }//map.put ( "Range", "0-0,-1".split ( "," ) ) ;//0-0,-1,1100-,500-600,601-999

        // If method 'POST' then read Content-Length worth of data
        if ( url[0].equalsIgnoreCase ( "POST" ) ) {
            int len = Integer.parseInt ( ( ( String[] ) map.get ( "Content-Length" ) )[0] ) ;
            char[] buf = new char[len] ;
            if ( rdr.read ( buf ) == len )
                line = String.copyValueOf(buf);
        } else if (url[0].equalsIgnoreCase ( "GET" ) ) {
            int idx = url[1].indexOf ( '?' ) ;
            if (idx != -1) {
                map.put ( "Context", new String[] { url[1].substring ( 1, idx ) } ) ;
                line = url[1].substring ( idx + 1 ) ;
            } else line = null;
        }
        
        if (line != null) {
            String[] match = line.split ( "\\&" ) ;
            for ( int i = 0; i < match.length; i++ ) {
                String[] params = new String[1] ;
                String[] tokens = match[i].split ( "=" ) ;
                switch ( tokens.length ) {
                case 0:
                    map.put ( "@".concat(match[i]), new String[] {} ) ;
                    break;
                case 1:
                    map.put ( "@".concat( tokens[0]), new String[] {} ) ;
                    break;
                default:
                    String name = "@".concat ( tokens[0] ) ;
                    if ( map.containsKey ( name ) ) {
                        params = ( String[] ) map.get ( name ) ;
                        String[] tmp = new String [ params.length + 1 ] ;
                        for ( int j = 0; j < params.length; j++ )
                            tmp[j] = params[j] ;
                        params = null ;
                        params = tmp ;
                    }
                    params[params.length - 1] = tokens[1].trim () ;
                    map.put ( name, params ) ;
                }
            }
        }

        return map ;
    }    
}